package com.jlh.com.jlh;

import com.intellij.lang.jvm.JvmParameter;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DocumentUtils {

    public enum ApiTagEnums {
        API("api", Pattern.compile("(\\s*@api\\s*\\{.+\\}\\s+)([^\\s]*)(\\s+.*)*", Pattern.DOTALL), 3, ""),
        GROUP("apiGroup", Pattern.compile("(\\s*@apiGroup\\s*)(\\w*)", Pattern.DOTALL), 2, "default"),
        VERSION("apiVersion", Pattern.compile("(\\s*@apiVersion\\s*)([\\w|\\.]*)", Pattern.DOTALL), 2, "1.0.0"),
        APIPARAMREMARK("apiParam", Pattern.compile("(\\s*@apiParam\\s*\\{.+\\}\\s+)([^\\s]*)(\\s+.*)*", Pattern.DOTALL), 3, ""),
        APIPARAM("apiParam", Pattern.compile("(\\s*@apiParam\\s*\\{.+\\}\\s+)([^\\s]*)(\\s+.*)*", Pattern.DOTALL), 2, "");


        ApiTagEnums(String tagName, Pattern pattern, int valueIndex, String defaultValue) {
            this.tagName = tagName;
            this.pattern = pattern;
            this.valueIndex = valueIndex;
            this.defaultValue = defaultValue;
        }

        private String tagName;
        private Pattern pattern;
        private int valueIndex;
        private String defaultValue;

        public String getTagName() {
            return tagName;
        }

        public Pattern getPattern() {
            return pattern;
        }

        public int getValueIndex() {
            return valueIndex;
        }

        public String getDefaultValue() {
            return defaultValue;
        }
    }

    /**
     * apitag info字段获取
     */
    public static String getDocInfo(PsiDocComment docComment, ApiTagEnums apiTagType) {
        if (docComment == null) {
            return apiTagType.getDefaultValue();
        }

        PsiDocTag apiTag = docComment.findTagByName(apiTagType.getTagName());

        if (apiTag == null) {
            return apiTagType.getDefaultValue();
        }


        return getDocInfo(apiTag.getText(), apiTagType);
    }

    private static String getDocInfo(String docComment, ApiTagEnums apiTagType) {
        String docText = docComment.substring(0, docComment.lastIndexOf("\n"));
        Matcher matcher = apiTagType.getPattern().matcher(docText);
        if (matcher.find()) {
            if (matcher.groupCount() >= apiTagType.getValueIndex()) {
                return matcher.group(apiTagType.getValueIndex());
            }
        }
        return apiTagType.getDefaultValue();
    }

    public static String getApiInfo(PsiDocComment psiComment, String methodType, String path) {
        String info = StringUtils.defaultIfBlank(DocumentUtils.getDocInfo(psiComment, DocumentUtils.ApiTagEnums.API),ApiTagEnums.API.defaultValue);
        return "@api {" + methodType + "} " + path + "" + info + "\n* ";
    }

    public static String getApiVersion(PsiDocComment psiDocComment) {
        String version = StringUtils.defaultIfBlank(DocumentUtils.getDocInfo(psiDocComment, ApiTagEnums.VERSION),ApiTagEnums.VERSION.defaultValue);
        return "@apiVersion " + version + "\n* ";
    }

    public static String getApiName(PsiMethod psiMethod, PsiClass psiClass) {
        String method = psiMethod.getName();
        String className = psiClass.getName();
        return "@apiName " + className + "-" + method + "\n* ";
    }

    public static String getApiGroup(PsiDocComment psiDocComment) {
        String group = StringUtils.defaultIfBlank(DocumentUtils.getDocInfo(psiDocComment, DocumentUtils.ApiTagEnums.GROUP),ApiTagEnums.GROUP.defaultValue);
        return "@apiGroup " + group + "\n* ";
    }

    public static String getApiParamExmple(PsiMethod psiMethod) {
        PsiDocComment psiComment = psiMethod.getDocComment();

        boolean isJSONRequest = Stream.of(psiMethod.getParameters())
                .anyMatch(m->m.getAnnotation("org.springframework.web.bind.annotation.RequestBody")!=null);
        String defaultStr;
        if (isJSONRequest) {
            defaultStr="@apiParamExample {json} Request-Example:\n" +
                    "* {}\n* ";
        }else {
            defaultStr="@apiParamExample {form-data} Request-Example:\n" +
                    "*  \n* ";
        }

        if (psiComment == null) {
            return defaultStr;
        }

        PsiDocTag apiParamExample = psiComment.findTagByName("apiParamExample");

        if (apiParamExample == null) {
            return defaultStr;
        }

        return apiParamExample.getText();

    }

    public static String getApiSuccessExample(PsiDocComment psiComment) {
        String defaultStr = "@apiSuccessExample {json} Success-Response:\n" +
                "* HTTP/1.1 200 OK\n" +
                "* { \n" +
                "*     \"success\": true\n" +
                "* }\n* ";
        if (psiComment == null) {
            return defaultStr;
        }

        PsiDocTag apiParamExample = psiComment.findTagByName("apiSuccessExample");

        if (apiParamExample == null) {
            return defaultStr;
        }

        return apiParamExample.getText();

    }

    public static String getApiParamString(PsiMethod psiMethod) {

        List<JvmParameter> collect = Stream.of(psiMethod.getParameters())
                .filter(m -> !m.getType().toString().contains("HttpServletRequest") && !m.getType().toString().contains("ModelMap")).collect(Collectors.toList());

        String lineTemplate = "@apiParam __type__ __name____info__ \n* ";

        PsiDocComment docComment = psiMethod.getDocComment();


        Map<String, String> oldDocMap = new HashMap<>();
        if (docComment != null) {
            Stream.of(docComment.findTagsByName("apiParam"))
                    .forEach(m -> {
                        String param = getDocInfo(m.getText(), ApiTagEnums.APIPARAM);
                        String remark = getDocInfo(m.getText(), ApiTagEnums.APIPARAMREMARK);
                        if (StringUtils.isNoneBlank(param)) {
                            oldDocMap.put(param, remark);
                        }
                    });
        }

        StringBuilder stringBuilder = new StringBuilder();

        for (JvmParameter parameter : collect) {
            PsiAnnotation requestParam = (PsiAnnotation) parameter.getAnnotation("org.springframework.web.bind.annotation.RequestParam");

            PsiAnnotationMemberValue required = requestParam != null ? requestParam.findDeclaredAttributeValue("required") : null;

            String paramName = requestParam != null && StringUtils.isNotBlank(requestParam.findDeclaredAttributeValue("value").getText()) ?
                    requestParam.findDeclaredAttributeValue("value").getText().replace("\"", "") : parameter.getName();


            String col = "{%s}";
            if (requestParam == null || (required != null && required.getText().equals("false"))) {
                col = "[%s]";
            }


            String paramTypeName = parameter.getType().toString().split(":")[1];
            paramTypeName = paramTypeName.replaceAll("<", "(")
                    .replaceAll(">", ")");
            String line = lineTemplate.replace("__name__", paramName)
                    .replace("__type__", String.format(col, paramTypeName))
                    .replace("__info__", oldDocMap.getOrDefault(paramName, ""));
            stringBuilder.append(line);
        }


        return stringBuilder.toString();

    }


}
